package com.template.mongo.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.template.mongo.persistence.dao.AccountDao;
import com.template.mongo.persistence.entity.Account;
import com.template.mongo.service.UserService;
import com.template.mongo.utils.PagingInfo;
import com.template.mongo.utils.PropertyResolver;
import com.template.mongo.utils.UserUtils;

@Service("userDetailService")
public class UserDetailServiceImpl implements UserService, UserDetailsService {

	static Logger logger = Logger.getLogger(UserDetailServiceImpl.class);

	@Autowired
	private AccountDao accountDao;

	@Autowired
	private AuthenticationManager springAuthManager;

	@Autowired
	private PropertyResolver propertyResolver;

	@Autowired
	private Md5PasswordEncoder md5PasswordEncoder;

	@SuppressWarnings("deprecation")
	@Override
	public UserDetails loadUserByUsername(String usrName)
			throws UsernameNotFoundException, DataAccessException {
		String rootUsrName = propertyResolver.getValue(UserUtils.KEY_ROOTNAME);
		if (usrName.equals(rootUsrName)) {
			String rootPwd = propertyResolver.getValue(UserUtils.KEY_ROOTPWD);
			return UserUtils.createAdvanceAdmin(rootUsrName,
					md5PasswordEncoder.encodePassword(rootPwd, null));
		} else if (usrName.equals("openUser")) {
			logger.debug("Open user logged in");
			return UserUtils.createOpenUser(usrName,
					md5PasswordEncoder.encodePassword("openpwd", null));
		} else {
			Account user = findByUserName(usrName);

			if (user != null) {
				if (user.getIsActive()) {
					return UserUtils.createActiveUser(user);
				}
			}

			throw new UsernameNotFoundException("Cannot find user", usrName);
		}
	}

	public Account findByUserName(String userName) {
		return accountDao.findByUserName(userName);
	}	

	public List<Account> findByEmail(String email) {
		return accountDao.findByEmail(email);
	}

	public Account findByMobile(String mobile) {
		return accountDao.findByMobile(mobile);
	}

	public List<Account> findByLevel(int level) {
		return accountDao.findByLevel(level);
	}
	
	public List<Account> findByIsActive(short isActive) {
		return accountDao.findByIsActive(isActive);
	}

	public List<Account> findByProperty(String property, Object value) {
		return null;
	}

	@Override
	public boolean checkEmail(String email) {
		Map<String, Object> mObject = new HashMap<String, Object>();
		mObject.put("email", email);
		return false;
	}
	
	public Account getCurrentLoggedInUser() {
		String rootUsrName = propertyResolver.getValue(UserUtils.KEY_ROOTNAME);
		Object principalObj = getSecurityContext().getAuthentication()
				.getPrincipal();
		if (principalObj != null
				&& principalObj instanceof UserDetails
				&& !((UserDetails) principalObj).getUsername().equals(
						rootUsrName)) {
			return findByUserName(((UserDetails) principalObj).getUsername());
		} else if (principalObj instanceof UserDetails
				&& ((UserDetails) principalObj).getUsername().equals(
						rootUsrName)) {
			Account ipUser = new Account();
			// ipUser.setName("Super admin");
			ipUser.setUserName(rootUsrName);
			ipUser.setPassword(md5PasswordEncoder.encodePassword(
					propertyResolver.getValue(UserUtils.KEY_ROOTPWD), null));
			ipUser.setLevel(5);
			return ipUser;
		}

		return null;
	}

	public SecurityContext getSecurityContext() {
		return SecurityContextHolder.getContext();
	}

	public boolean authenticate(String userName, String password,
			HttpServletRequest request) {
		UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(
				userName, password);
		userAuth.setDetails(new WebAuthenticationDetails(request));

		Authentication authentication = springAuthManager
				.authenticate(userAuth);

		if (authentication.isAuthenticated()) {
			getSecurityContext().setAuthentication(authentication);
			return true;
		}

		return false;
	}

	@Override
	public boolean authenticate(String userName, String pwd) {
		UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(
				userName, pwd);
		Authentication authentication = springAuthManager
				.authenticate(userAuth);

		if (authentication.isAuthenticated()) {
			getSecurityContext().setAuthentication(authentication);
			return true;
		}

		return false;
	}

	@Transactional(rollbackFor = Exception.class)
	public void save(Account u) {
		accountDao.save(u);
	}

	@Transactional(rollbackFor = Exception.class)
	public void update(Account u) {
		accountDao.update(u);
	}

	@Override
	@Transactional
	public void deleteUser(String userName, boolean isActive) {
		Account iu = accountDao.findByUserName(userName);
		if (isActive == false) {
			iu.setIsActive(true);
		} else {
			iu.setIsActive(false);
		}
	}

	@Override
	public boolean checkExists(String usrName) {
		Map<String, Object> mObject = new HashMap<String, Object>();
		mObject.put("userName", usrName);
		return false;
	}

	@Override
	public Account findUserByEmail(String email) {
		return accountDao.findUserByEmail(email);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean savePwd(String u, String pwd) {
		try {
			Account iu = accountDao.findByUserName(u);
			iu.setPassword(pwd);
			return true;
		} catch (Exception e) {
			logger.error("Fail to update user for " + u + ":", e);
			return false;
		}
	}

	@Override
	public List<Account> getAllUsers(PagingInfo pInfo) {
		return null;
	}

	@Override
	public List<Account> getUsers(String kw, PagingInfo pInfo) {
		return null;
	}
	
	@Override
	public boolean checkUserNameIgnore(String userName , String userNameOld) {
		return accountDao.checkUserNameIgnore(userName, userNameOld);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean delete(String userName) {
		return false;
	}
	
	@Override
	public List<Account> getUsers(String kw, long departmentId, PagingInfo pInfo) {
		return accountDao.getUsers(kw,departmentId, pInfo);
	}

	@Override
	public List<Account> findAll() {
		return null;
	}

}
